/**
 * Copyright 2002 Cordys R&D B.V. 
 * 
 * This file is part of the Cordys .NET Connector. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 /****************************************************************************************
  Defintions of unmanaged API  for the manipulating the XML Document and Node 
****************************************************************************************/

#include "windows.h"
#include "xmlForDotnet.h"
#include "Unmanaged.h"

using namespace System;


/*****************************************************************************************
	#pragma unmanaged is directive that should be used for making this function as an
	unmanaged function. for defining the managed function " #pragma managed " should 
	be used. until the aleternative directive is defined, the current directive applies
	to all the functions that are followed by this directive.
*****************************************************************************************/

#pragma unmanaged 
int UMCreateDocument()
{
	tHXmlDoc doc = xmlNewDoc();

	 if ( doc )
	 {
		#ifdef HASH_ELEMENT
			xmlSetGHPNamedDoc(doc, (tChar*)NamedGhpDocForJava);
		#endif
		xmlMTSave(doc);
		return(getXmlDocId(doc));
	}
	else
	{
		return(0);
	}
}

/*
 * Unmanaged functin which reads the byte stream and returns the 
 * the  root reference
 */ 
int UMReadBytes(int doc_handle, unsigned char *pbytes, int sizeOfBytes)
{
	tChar errorMsg[1024];
	int xmlRoot = -1;
	unsigned char * p = pbytes;
	
	xmlRoot = getXmlNodeId(
	xmlReadFromByteArray( getXmlDoc(doc_handle),
							  (tChar*)p,
							  sizeOfBytes,
							  (tChar*)errorMsg,
							  sizeof(errorMsg)
							  ));
 		
	if (!xmlRoot)
	{
		xmlRoot = -1;		
		throw ((const char* )errorMsg);
	}
	return xmlRoot;
}

/*
 * Unmanaged function which takes the document handle and the filename as 
 * parameter.It reads the xml from the specified file.It throws the XMLException 
 * if any error occurs.
 */
int UMReadFromFile(int doc_handle,unsigned char* pFileName)
{
	tGstDoc	stream;
	tChar errorMsg[1024];
	tHXmlDoc document = getXmlDoc(doc_handle);
	int	xmlRoot = -1;
	
	gstInitDoc(&stream);
	int res = gstSetFileName(&stream,pFileName, (tChar*) "r");
	
	if (res)
	{	
		xmlRoot = -1;
		sprintf((char*) errorMsg, "Failed to find file %s",pFileName);
		gstEndDoc(&stream);
		throw ((const char*) errorMsg);
	}//End of if
	
	xmlRoot = getXmlNodeId(xmlRead(document, &stream, errorMsg, sizeof(errorMsg)));

	if (!xmlRoot)
	{		
		xmlRoot = -1;
		gstEndDoc(&stream);
		throw ((const char*) errorMsg);
	}
	return xmlRoot;
}

/* Function which creates the Node of type Element Type,and appends to the 
 * parent as a child
 */
int UMCreateElementNode(int doc_handle, unsigned char * tagString , int parentNode)
{
	tXmlDoc*	document = getXmlDoc(doc_handle);
	tHXmlNode	parent = getXmlNode(parentNode);

	tHXmlNode	node = xmlNewElement(document, (tChar*) tagString , parent);
	return getXmlNodeId(node);
}

/* Function which creates the Node of type Element Type,and appends to the 
 * parent as a child
 */
// New for BP
int UMCreateElementNodeWithParentNS(int doc_handle, unsigned char * tagString , int parentNode)
{
	tXmlDoc*	document = getXmlDoc(doc_handle);
	tHXmlNode	parent = getXmlNode(parentNode);

	// if prefix is null AND namespaceURI is null, then element with parent namespace-details is created
	tHXmlNode	node = xmlNewElementNS(document, (tChar*) tagString, NULL, NULL, NULL, parent);
	return getXmlNodeId(node);
}

/* Function which creates the Node of type Data Type,and appends to the 
 * parent as a child
 */
int UMCreateDataNode(int doc_handle, unsigned char * textStr , int parentNode)
{
		tXmlDoc*	document = getXmlDoc(doc_handle);
		tHXmlNode	parent = getXmlNode(parentNode);

		tHXmlNode	node = xmlNewData(document, (tChar*) textStr, parent);
		return getXmlNodeId(node);
}

/* Function which creates an Element of type Data Type,and appends to the 
 * parent as a child
 */
int UMCreateDataElement(int doc_handle, unsigned char * elementStr, unsigned char * textStr , int parentNode)
{
		tXmlDoc*	document = getXmlDoc(doc_handle);
		tHXmlNode	parent = getXmlNode(parentNode);

		tHXmlNode	node = xmlNewDataElement(document, (tChar*)elementStr, (tChar*) textStr, parent);
		return getXmlNodeId(node);
}

/* Function which creates an Element of type Data Type,and appends to the 
 * parent as a child
 */
// New for BP
int UMCreateDataElementWithParentNS(int doc_handle, unsigned char * elementStr, unsigned char * textStr , int parentNode)
{
		tXmlDoc*	document = getXmlDoc(doc_handle);
		tHXmlNode	parent = getXmlNode(parentNode);

		//tHXmlNode	node = xmlNewDataElement(document, (tChar*)elementStr, (tChar*) textStr, parent);
		tHXmlNode	node = xmlNewElementNS(document, (tChar*) elementStr, (tChar*) textStr, NULL, NULL, parent);
		return getXmlNodeId(node);
}

/*
 * Locks the Document making it thread Safe.
 */
void UMLockDoc(int doc_handle)
{
	tXmlDoc * doc = getXmlDoc(doc_handle);
	xmlLockDoc(doc);
}

/*
 * Unlocking the locked Document, so that this document can be accessed 
 * by other threads.
 */
void UMUnLockDoc(int doc_handle)
{
	tXmlDoc * doc = getXmlDoc(doc_handle);
	xmlUnlockDoc(doc);
}

/* Gives the Prefix of the indexed Attribute in the Node of type Element
*/
int  UMXmlAttrPrefix(int node , int index ,unsigned char * prefix)
{	
	if (xmlGetAttributePrefix(getXmlNode(node), index, (tChar*) prefix, sizeof(prefix))==0)
	{
		return 1;
	}
	else
	{
		return NULL;
	}
}

/*
 * Returns the data depending on the Node Type of the Node.
 * if no data is found then the NULL is returned
 */
char* UMXmlGetData(int node , bool* virtualAlloc)
{
	tXmlTypes xmlType = xmlGetType(getXmlNode(node));
	
	switch ( xmlType )	{
		case xmlElement:
		case xmlDtd:	{
			
			return UMDataToCharArray(node,0,virtualAlloc);
			}

		case xmlData:
		case xmlPi:	{
			char* data = (char*) xmlGetDataHandle(getXmlNode(node), (int*)0);
			if (data)	{				
				 return data;
			}
		}
	}
	return NULL;
}

/*
 *  Returns the data of the first child of node that has the specified tagname
 */
const char* UMXmlGetDataElement(int node ,const unsigned char * tagName, bool* virtualAlloc)
{
	const char* data = (const char *)UMDataToCharArray(node,(char*)tagName, virtualAlloc);	
	return data;	
}

/*
 *  Returns the data of the first child of node that has the specified tagname
 */
// New for BP
const char* UMXmlGetDataElementNS(int node ,const unsigned char * tagName, const unsigned char * uri, bool* virtualAlloc)
{
	const char* data = (const char *)UMDataToCharArrayNS(node,(char*)tagName, (char*)uri, virtualAlloc);	
	return data;	
}

/*
 * Writes the range of Node to the specifief File with or without indentation depending 
 * on the flag pretty.If any error occurs during the writing then Exception is thrown.
 */
void UMWriteToFile(int fromNode ,int toNode ,const unsigned char * pFileName ,int pretty)
{
	tHXmlNode	from = getXmlNode(fromNode);
	tHXmlNode	to = getXmlNode(toNode);	
	tChar errorMsg[1024];
	if (xmlWriteToFile(from, to, (tChar*)pFileName)<0)
	{	
		sprintf((char*) errorMsg, "Failed to write to file %s",pFileName);
		throw (const char*)errorMsg;		
	}
}




/*char*  UMXmlWriteToByteArray(int fromNode,int toNode, int pretty)
{
	tHGstDoc stream;
	tHXmlNode from = getXmlNode(fromNode);
	tHXmlNode to = getXmlNode(toNode);
	tHXmlDoc xmlDoc;	
	char * tempBuff ;

	if ( from )	{		
		xmlDoc = xmlGetDoc(from);		
	} else	{
		xmlDoc = xmlGetDoc(to);
	}
	if ( xmlDoc )	{		
		xmlLockDoc(xmlDoc);
	}	
	
	if ( (stream = xjaGetInMemoryStream()) )	{
		gstSetLocation(stream, 0);
		
		int length = (int)xmlWrite(from, to, stream, (tXmlSerializeOptions) pretty);
		
		if (length >= 0)	{

			const char* internalBytes  = (const char*) gstGetHandle(stream);
			tempBuff = new char[length];

			//memcpy((void*)tempBuff,(void*)internalBytes,length);
			int index = 0;
			for(;(index<length && internalBytes[index] != '\0') ;index++)
			{
				tempBuff[index] = internalBytes[index];
			}
			tempBuff[length]='\0';
		}
		xjaReleaseInMemoryStream();
	}
	if ( xmlDoc )	{
		xmlUnlockDoc(xmlDoc);
	}
	return tempBuff;
}*/

//To  refine more 
#pragma unmanaged
const char*  UMXmlWriteToByteArray(int fromNode,int toNode, int pretty) 
{
	tHGstDoc stream;
	tHXmlNode from = getXmlNode(fromNode);
	tHXmlNode to = getXmlNode(toNode);
	tHXmlDoc xmlDoc;
	char * tempBuff ;
	//jbyteArray javaByteArray = 0;

	if ( from )	{
		xmlDoc = xmlGetDoc(from);
	} else	{
		xmlDoc = xmlGetDoc(to);
	}
	if ( xmlDoc )	{
		xmlLockDoc(xmlDoc);
	}
	 
	if ( (stream = xjaGetInMemoryStream()) )	{
		gstSetLocation(stream, 0);
		int length = (int)xmlWrite(from, to, stream, (tXmlSerializeOptions) pretty);
		if (length >= 0)	{
			//internalBytes = (char*) gstGetHandle(stream);
			const char* ss  = (const char*) gstGetHandle(stream);

			tempBuff = new char[length];
			int index = 0;			

			for(;(index<length && ss[index] != '\0') ;index++)
			{	
				tempBuff[index] = ss[index];
			}
			tempBuff[index]='\0';

		}
		xjaReleaseInMemoryStream();
	}
	if ( xmlDoc )	{
		xmlUnlockDoc(xmlDoc);
	}
	return (const char*)tempBuff;
}

/*
 * Writes the range of nodes fromNode to toNode to the Stream provided.
 * The provided stream should be thread safe stream.
 */
int UMXmlWriteToStream(int fromNode,int toNode, int pretty,tHGstDoc stream)
{	
	int length =0;
	tHXmlNode from = getXmlNode(fromNode);
	tHXmlNode to = getXmlNode(toNode);
	tHXmlDoc xmlDoc;

	if ( from )	{
		xmlDoc = xmlGetDoc(from);
	} else	{
		xmlDoc = xmlGetDoc(to);
	}
	if ( xmlDoc )	{
		xmlLockDoc(xmlDoc);
	}

	if (stream)	{
		length = (int)xmlWrite(from, to, stream, (tXmlSerializeOptions) pretty);
	}

	if ( xmlDoc )	{
		xmlUnlockDoc(xmlDoc);
	}
	return length ;
}

/*
 * Converts the data of the Node which has the specified name as tagName and returns as Char array
 * Depending upon the size, the Allocation of the data is done either using CRT Functions 
 * or using Virtual Functions.
 */
char*  UMDataToCharArray(int node ,const char * tag ,bool* virtualAlloc)
{
	tChar *buf;
	long  size ;
	char* ret = NULL;

	tHXmlNode xmlNode = getXmlNode(node);
	
	if ( tag && !(xmlNode = xmlGetElement(xmlNode, (tChar*)tag)) )	{
		goto onExit;
	}
	size = xmlGetDataLengthFromList(xmlNode, xmlNode, 9999);
	
	if(size == 1){
		goto onExit;
	}
	if ( size < 10000 )	{		
		buf = (tChar*)malloc(size);
		*virtualAlloc = false;
	} else	{
		size = glRoundup(size, 64*1024);
#ifdef WIN32
		buf = (tChar*)VirtualAlloc((LPVOID)0, size, MEM_COMMIT, PAGE_READWRITE);
#else
		buf = (tChar *)valloc(size);
#endif
		if ( !buf )	{
			goto onExit;
		}
		*virtualAlloc = true;
	}
	if ( xmlGetDataFromList(xmlNode, xmlNode, buf, size, 9999) >= 0 )	{		
		ret = (char*)buf;
		
	}
	/*if ( virtualAlloc )	{
#ifdef WIN32
		//VirtualFree(buf, 0, MEM_RELEASE);
		 virtualAlloc = true;
#else
		free((void*)buf);
#endif
	}*/
onExit:;	
	return((char*)ret);
}

/*
 * Converts the data of the Node which has the specified name as tagName and returns as Char array
 * Depending upon the size, the Allocation of the data is done either using CRT Functions 
 * or using Virtual Functions.
 */
//New for BP. This is a bad implementation. Code is dupilcated in the above method.
char*  UMDataToCharArrayNS(int node, const char * tag, const char * uri, bool* virtualAlloc)
{
	tChar *buf;
	long  size ;
	char* ret = NULL;

	tHXmlNode xmlNode = getXmlNode(node);
	
	if ( tag && !(xmlNode = xmlGetElementNS(xmlNode, (tChar*)tag, (tChar*)uri) ) )	{
		goto onExit;
	}
	size = xmlGetDataLengthFromList(xmlNode, xmlNode, 9999);
	
	if(size == 1){
		goto onExit;
	}
	if ( size < 10000 )	{		
		buf = (tChar*)malloc(size);
		*virtualAlloc = false;
	} else	{
		size = glRoundup(size, 64*1024);
#ifdef WIN32
		buf = (tChar*)VirtualAlloc((LPVOID)0, size, MEM_COMMIT, PAGE_READWRITE);
#else
		buf = (tChar *)valloc(size);
#endif
		if ( !buf )	{
			goto onExit;
		}
		*virtualAlloc = true;
	}
	if ( xmlGetDataFromList(xmlNode, xmlNode, buf, size, 9999) >= 0 )	{		
		ret = (char*)buf;
		
	}
	/*if ( virtualAlloc )	{
#ifdef WIN32
		//VirtualFree(buf, 0, MEM_RELEASE);
		 virtualAlloc = true;
#else
		free((void*)buf);
#endif
	}*/
onExit:;	
	return((char*)ret);
}

